using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using UnityEngine;
using YooAsset;
[CreateAssetMenu(fileName = "YAssetsLoader", menuName = "Scriptable/Y/YAssetsLoader", order = 1)]

public class YAssetsLoader : ScriptableObject
{

    private class RemoteServices : IRemoteServices
    {
        public string FallbackURL { get; set; }
        public string MainURL { get; set; }
        public string GetRemoteFallbackURL(string fileName)
        {
            return $"{FallbackURL}/{fileName}";
        }

        public string GetRemoteMainURL(string fileName)
        {
            return $"{MainURL}/{fileName}";

        }
    }
    public string ScriptsTag = "Script";
    public string PackageName;
    public string HostDefault;
    public string HostFallback;

    public bool PackageIsInit;
    public bool PackageIsRequestVersion;
    public bool PackageIsUpdateManifest;


    public string CurrentVersion;
    ResourcePackage ResourcePackage => YooAssets.GetPackage(PackageName) ?? YooAssets.CreatePackage(PackageName);

    private async Task InitPackage()
    {
        var package = ResourcePackage;

        var buildResult = EditorSimulateModeHelper.SimulateBuild(PackageName);
        var packageRoot = buildResult.PackageRootDirectory;
        var editorFileSystemParams = FileSystemParameters.CreateDefaultEditorFileSystemParameters(packageRoot);
        var initParameters = new EditorSimulateModeParameters();
        initParameters.EditorFileSystemParameters = editorFileSystemParams;
        var initOperation = package.InitializeAsync(initParameters);
        await initOperation.Task;
        if (initOperation.Status == EOperationStatus.Succeed)
        {
            Debug.Log($"ASSETS:INIT:???:EDITOR");
            PackageIsInit = true;

        }
        else
        {
            Debug.LogError($"ASSETS:INIT:???:EDITOR");


        }
    }
    public async Task Init()
    {
#if UNITY_EDITOR
        await InitPackage();
        return;
#endif
        var package = ResourcePackage;

        if (package.InitializeStatus == EOperationStatus.Succeed)
        {
            return;
        }
        IRemoteServices remoteServices = new RemoteServices
        {
            FallbackURL = HostFallback,
            MainURL = HostDefault
        };

        var remotoSystemParams = FileSystemParameters.CreateDefaultCacheFileSystemParameters(remoteServices);
        remotoSystemParams.AddParameter(FileSystemParametersDefine.INSTALL_CLEAR_MODE, EOverwriteInstallClearMode.None);

        var localSystemParams = FileSystemParameters.CreateDefaultBuildinFileSystemParameters();
        localSystemParams.AddParameter(FileSystemParametersDefine.COPY_BUILDIN_PACKAGE_MANIFEST, true);

        var initParameters = new HostPlayModeParameters();
        initParameters.BuildinFileSystemParameters = localSystemParams;
        initParameters.CacheFileSystemParameters = remotoSystemParams;


        var initOperation = package.InitializeAsync(initParameters);
        await initOperation.Task;
        Debug.Log($"ASSETS:INIT:{initOperation.ProcessTime}");


        if (initOperation.Status == EOperationStatus.Succeed)
        {
            Debug.Log($"ASSETS:INIT:???");
            PackageIsInit = true;
        }
        else
        {
            Debug.LogError($"ASSETS:INIT:???{initOperation.Error}");
        }

    }

    public async Task<string> RequestVersion()
    {
        if (!PackageIsInit)
        {
            Debug.LogError($"ASSETS:GETVERSION:{PackageName}:???,?????????????");
            return string.Empty;
        }
        var package = ResourcePackage;

        var operation = package.RequestPackageVersionAsync(timeout: 10);
        await operation.Task;
        Debug.Log($"ASSETS:GETVERSION:{PackageName}:{operation.ProcessTime}");

        if (operation.Status == EOperationStatus.Succeed)
        {
            PackageIsRequestVersion = true;
            Debug.Log($"ASSETS:GETVERSION:{PackageName}:{operation.PackageVersion}:???");
            return operation.PackageVersion;
        }
        else
        {
            Debug.LogError($"ASSETS:GETVERSION:{PackageName}:???{operation.Error}");
            return string.Empty;
        }
    }
    public async Task SetPackageVersion(string version)
    {
        if (!PackageIsInit)
        {
            Debug.LogError($"ASSETS:SETVERSION:{PackageName}:???,?????????????");
            return;
        }
        var package = ResourcePackage;

        var operation = package.UpdatePackageManifestAsync(version);
        await operation.Task;
        Debug.Log($"ASSETS:UPDATE:MANIFEST:{version}:{operation.ProcessTime}");

        if (operation.Status == EOperationStatus.Succeed)
        {
            PackageIsUpdateManifest = true;
            CurrentVersion = version;
            Debug.Log($"ASSETS:UPDATE:MANIFEST:{version}:???");
            return;
        }
        else
        {
            Debug.LogError($"ASSETS:UPDATE:MANIFEST:{version}:???{operation.Error}");
            return;
        }
    }
    public Dictionary<string, ResourceDownloaderOperation> ResourceDownloaders { get; } = new Dictionary<string, ResourceDownloaderOperation>();
    //public Dictionary<string, AssetHandle> AssetHandles { get; } = new Dictionary<string, AssetHandle>();
    public ResourceDownloaderOperation GetResourceDownloader(string path)
    {
        ResourceDownloaders.TryGetValue(path, out var downloader);
        if (downloader != null)
        {
            if (downloader.Status != EOperationStatus.Failed)
            {
                return downloader;
            }
        }
        var package = ResourcePackage;
        var assetsInfo = package.GetAssetInfo(path);
        if (assetsInfo == null) return null;
        downloader = package.CreateBundleDownloader(assetsInfo, 4, 10);
        ResourceDownloaders[path] = downloader;
        downloader.BeginDownload();
        return downloader;
    }
    public async Task<AssetHandle> GetAssetsHandle<T>(string path) where T : UnityEngine.Object
    {
        var package = ResourcePackage;

        if (package.IsNeedDownloadFromRemote(path))
        {
            GetResourceDownloader(path);
            return null;
        }


        var assetsInfo = package.GetAssetInfo(path);
        var operation = package.LoadAssetAsync<T>(path);
        await operation.Task;
        if (operation.Status == EOperationStatus.Succeed)
        {
            Debug.Log($"ASSETS:LOAD:{path}:??????:");
            return operation;
        }
        else
        {
            Debug.LogError($"ASSETS:LOAD:{path}:???{operation.LastError}");
            return null;
        }
    }
    public async Task<SceneHandle> GetScene(string path)
    {
        var package = ResourcePackage;

        if (package.IsNeedDownloadFromRemote(path))
        {
            GetResourceDownloader(path);
            return null;
        }
        var assetsInfo = package.GetAssetInfo(path);
        var operation = package.LoadSceneAsync(assetsInfo);
        await operation.Task;
        if (operation.Status == EOperationStatus.Succeed)
        {
            Debug.Log($"ASSETS:LOAD:{path}:??????:");
            return operation;

        }
        else
        {
            Debug.LogError($"ASSETS:LOAD:{path}:???{operation.LastError}");
            return null;
        }

    }
    public AssetInfo[] GetAssetsInfosByTag(string tag)
    {
        var package = ResourcePackage;
        return package.GetAssetInfos(tag);
    }

    public async Task LoadScripts()
    {
        var scriptInfos = GetAssetsInfosByTag(ScriptsTag);
        var scriptDownloaders = scriptInfos.Select(item => GetResourceDownloader(item.Address)).ToArray();

        await Task.WhenAll(scriptDownloaders.Select(item => item.Task));

        foreach (var bytes in scriptInfos)
        {
            if (!bytes.Address.EndsWith(".dll"))
            {
                continue;
            }
            var dllName = Path.GetFileNameWithoutExtension(bytes.Address);

            var dll = await GetAssetsHandle<TextAsset>($"{dllName}.dll");
            var pdb = await GetAssetsHandle<TextAsset>($"{dllName}.pdb");
            if (dll == null)
            {
                throw new System.Exception("????????????????????");
            }
            if (dll.AssetObject is TextAsset dllBytes)
            {

                if (false && pdb.AssetObject is TextAsset pdbBytes)
                {
                    Assembly.Load(dllBytes.bytes, pdbBytes.bytes);
                }
                else
                {
                    Assembly.Load(dllBytes.bytes);
                }
            }
            dll?.Release();
            pdb?.Release();

        };
    }
}